package com.scs.application.modules.sys.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.scs.application.core.consts.ErrorCode;
import com.scs.application.core.exception.BusinessException;
import com.scs.application.core.service.BaseServiceImpl;
import com.scs.application.core.utils.DateUtils;
import com.scs.application.core.utils.StringUtils;
import com.scs.application.core.utils.UtilCommon;
import com.scs.application.modules.base.entity.Matr;
import com.scs.application.modules.base.service.MatrService;
import com.scs.application.modules.sys.dto.LodoopPrintRFIDDTO;
import com.scs.application.modules.sys.entity.Label;
import com.scs.application.modules.sys.entity.Printer;
import com.scs.application.modules.sys.mapper.PrinterMapper;
import com.scs.application.modules.sys.model.RestResult;
import com.scs.application.modules.sys.service.LabelService;
import com.scs.application.modules.sys.service.PrinterService;
import com.scs.application.modules.wm.entity.Stock;
import com.scs.application.modules.wm.entity.StockLock;
import com.scs.application.modules.wm.enums.UniqueCodeType;
import com.scs.application.modules.wm.service.StockLockService;
import com.scs.application.modules.wm.service.StockService;
import com.scs.application.modules.wm.utils.UniqueCodeUtils;
import com.zebra.sdk.comm.Connection;
import com.zebra.sdk.comm.ConnectionException;
import com.zebra.sdk.comm.ResponseValidator;
import com.zebra.sdk.comm.TcpConnection;
import com.zebra.sdk.printer.ZebraPrinter;
import com.zebra.sdk.printer.ZebraPrinterFactory;
import com.zebra.sdk.printer.ZebraPrinterLanguageUnknownException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.text.ParseException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * <p>
 * 服务实现类
 * </p>
 */
@Service
public class PrinterServiceImpl extends BaseServiceImpl<PrinterMapper, Printer> implements PrinterService {
    protected final Logger logger = LoggerFactory.getLogger(this.getClass());
    @Autowired
    LabelService labelService;
    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    StockService stockService;

    @Autowired
    StockLockService stockLockService;


    @Autowired
    MatrService matrService;

    @Override
    public RestResult printLabel(String warehouseId, String labelKey, Object data) {
        Label label = labelService.getOne(Wrappers.<Label>query().eq("code", labelKey));
        if (label == null) {
            throw new BusinessException("labelKey 未找到：" + labelKey);
        }
        Printer printer = this.getOne(Wrappers.<Printer>query()
                .eq("warehouse_id", warehouseId)
                .eq("label_code", labelKey));
        if (printer == null) {
            throw new BusinessException("labelKey: " + labelKey + " 并未在当前工位下检索到可用的打印机！");
        }
        Map para = new HashMap();
        para.put("label", getLabelPara(label, printer.getName()));
        para.put("data", data);
        try {
            ResponseEntity<RestResult> responseEntity = restTemplate.postForEntity(
                    printer.getUrl(), para, RestResult.class);
            RestResult result = responseEntity.getBody();
            return result;
        } catch (Exception e) {
            throw new BusinessException("调用打印错误！请检查打印服务是否已正确启动！");
        }
    }
    @Override
    public void SavePrinter(Printer printer) {
        Printer p1 = this.getOne(Wrappers.<Printer>query()
                .eq("warehouse_id", printer.getWarehouseId())
                .eq("name", printer.getName()));
        if (p1 == null) {
            this.save(printer);
        }
        else {
            p1.setUrl(printer.getUrl());
            this.updateById(p1);
        }
    }

    /**
     * 得到label的参数
     *
     * @param label
     * @return
     */
    private Map getLabelPara(Label label, String printerName) {
        Map para = new HashMap();
        para.put("app_key", "spd3");
        para.put("code", label.getCode());
        para.put("version", label.getVersion());
        para.put("mversion", label.getGmtModified());
        para.put("type", label.getType());
        para.put("printerName", printerName);
        return para;
    }


    /**
     * @Description：替换指令里面的占位符为具体数值
     * <pre>
     *     占位符规则 key
     *     替换说明  匹配到一个占位符，替换
     *     未匹配到 忽略
     *     匹配到多个 报错
     * </pre>
     */
    private String replaceLabelCode(String labelCOde,String key, String value) {
        if(StringUtils.isBlank(labelCOde) ||  labelCOde.indexOf(key) == -1) return labelCOde;
        if (StringUtils.isBlank(value)) value = "无";
        value = UtilCommon.getStringPre(value,26);
//        if(labelCOde.indexOf(key) != labelCOde.lastIndexOf(key)) throw new BusinessException("打印机配置指令模板格式错误，占位符重复【"+key+"】");
        return labelCOde.replaceAll(key,value);

    }

    private boolean printRFIDAndWait(Connection printerConnection, LodoopPrintRFIDDTO lodoopPrintRFIDDTO) throws ConnectionException, ParseException {
        final boolean[] result = {true};
        //上一次调用打印时间
        Date printRfidLast = DateUtils.nowDate("yyyy-MM-dd HH:mm:ss");
        byte[] ret = printerConnection.sendAndWaitForValidResponse(lodoopPrintRFIDDTO.getSendData().getBytes(), lodoopPrintRFIDDTO.getPrinter().getWaittime() * 1000, lodoopPrintRFIDDTO.getPrinter().getWaittime() * 1000, new ResponseValidator() {
            public boolean isResponseComplete(byte[] arg0) {
                //系统时间距离上次打印时间超过 打印配置的等待时间+3秒，则终止所有打印
                int diff = DateUtils.calLastedTime(printRfidLast, DateUtils.nowDate("yyyy-MM-dd HH:mm:ss"));
                if (diff > lodoopPrintRFIDDTO.getPrinter().getWaittime()) {
                    throw new BusinessException("解析返回值失败，等待时间【" + diff + "】已超过打印机配置的等待时间【" + lodoopPrintRFIDDTO.getPrinter().getWaittime() + "】");
                }

                String oldRfid = new String(arg0);
                //校验读取到的RFID格式
                logger.info("printRFID.rfidold={}", oldRfid);
                logger.debug("printRFID.rfidold={}", oldRfid);

//                int f4Index = rfid.lastIndexOf("F4");
//                if (f4Index == -1)
//                    throw new BusinessException(lodoopPrintRFIDDTO.getMsgPre() + "RFID读取失败，读取数据内容【" + rfid + "】");
//
//                rfid = rfid.substring(f4Index);
//                rfid = rfid.substring(0, 11);
                //0000000000000F40004681360000000000000

                String rfid = UniqueCodeUtils.getRFID(oldRfid);
                if (StringUtils.isBlank(rfid)) {
                    throw new BusinessException(lodoopPrintRFIDDTO.getMsgPre() + "RFID读取失败，读取数据内容【" + oldRfid + "】");
                }
                // 判断唯一码类型
                logger.info("printRFID.rfidnew={}", rfid);
                logger.debug("printRFID.rfidnew={}", rfid);
                UniqueCodeType uniqueCodeType = UniqueCodeUtils.getUniqueCodeType(rfid);
                if (uniqueCodeType != UniqueCodeType.RFID)
                    throw new BusinessException(lodoopPrintRFIDDTO.getMsgPre() + "RFID读取失败，读取数据内容【" + rfid + "】");

                //校验RFID是否已被绑定
                Stock stockRfid = stockService.getStockByUniqueCode(rfid);
                if (stockRfid != null) {
                    throw new BusinessException(lodoopPrintRFIDDTO.getMsgPre() + "RFID【" + rfid + "】已被【" + stockRfid.getSn() + "】绑定");
                }
                //RFID与SN进行绑定
                if (!stockService.update(Wrappers.<Stock>update().eq("id", lodoopPrintRFIDDTO.getStock().getId()).set("rfid", rfid))) {
                    throw new BusinessException(lodoopPrintRFIDDTO.getMsgPre() + "RFID绑定失败");
                }
                lodoopPrintRFIDDTO.getSnArrSuccess().add(lodoopPrintRFIDDTO.getStock().getSn());
                result[0] = false;
                lodoopPrintRFIDDTO.addNumSuccess();
                return true;
            }
        });
        return result[0];
    }


    @Override
    public RestResult printRFID(JSONArray snArr, String printerId) {
        RestResult result = RestResult.ok();
        Printer printer = this.getById(printerId);
        if (printer == null) {
            throw new BusinessException("打印异常-打印机不可用-打印机配置不存在或者已删除");
        }
        if (!"rfid".equalsIgnoreCase(printer.getTypeKey())) {
            throw new BusinessException("打印异常-打印机不可用-非RFID打印机不支持直接打印");
        }
        if (StringUtils.isBlank(printer.getLabelCode())) {
            throw new BusinessException("打印异常-打印机不可用-打印机配置指令模板为空");
        }
        if (StringUtils.isBlank(printer.getUrl())) {
            throw new BusinessException("打印异常-打印机不可用-打印机配置访问地址为空");
        }
        if (printer.getPort() < 1) {
            throw new BusinessException("打印异常-打印机不可用-打印机配置端口为空");
        }
        Connection printerConnection = null;
        ZebraPrinter zebraPrinter = null;
        String sendData;
        LodoopPrintRFIDDTO lodoopPrintRFIDDTO = null;
        synchronized (printerId.intern()) {
            try {
                printerConnection = new TcpConnection(printer.getUrl(), printer.getPort());
                printerConnection.open();
                zebraPrinter = ZebraPrinterFactory.getInstance(printerConnection);
                //打印成功数量
                int numSuccess = 0;
                String breakMsg = "";
                Date date = new Date();
                for (Object sn : snArr) {
                    //库存校验
                    String msgPre = "SN【" + sn + "】绑定失败(后续打印任务已停止), ";
                    Stock stock = stockService.getStockByUniqueCode(sn + "");
                    if (stock == null) {
                        throw new BusinessException("打印异常-数据校验失败 " + msgPre + "未找到库存记录");
                    }
                    if (stock.getSkuQty() <= 0) {
                        throw new BusinessException("打印异常-数据校验失败 " + msgPre + "库存数量为0");
                    }
                    if (StringUtils.isNotBlank(stock.getRfid())) {
                        throw new BusinessException("打印异常-数据校验失败 " + msgPre + "已绑定RFID【" + stock.getRfid() + "】，请先解绑");
                    }
                    // 锁定校验
                    StockLock stockLock = stockLockService.getOne(new QueryWrapper<StockLock>().eq("stock_id", stock.getId()));
                    if (stockLock != null) {
                        throw new BusinessException("打印异常-数据校验失败 " + msgPre + "库存已被锁定");
                    }
                    //效期校验
                    if (DateUtils.compareDate(stock.getExpDate(), date)) {
                        throw new BusinessException("该SN【%s】效期【%s】已过期", stock.getSn(), DateUtils.format(stock.getExpDate()));
                    }
                    Matr matr = matrService.getById(stock.getMatrId());
                    if (matr == null) {
                        throw new BusinessException("打印异常-数据校验失败 " + msgPre + "对应耗材不存在或者已被删除");
                    }


                    sendData = printer.getLabelCode();

                    //替换占位符
                    String endStr = "低耗";
                    if (stock.getFlagOmoc() != null && stock.getFlagOmoc()) endStr = "高耗";

                    //耗材编码
                    sendData = replaceLabelCode(sendData, "matrCode", matr.getMscmCode() + "(" + endStr + ")");
                    //耗材名称

                    sendData = replaceLabelCode(sendData, "stockName", stock.getMatrName().replaceAll("~", "～"));
                    //耗材规格
                    sendData = replaceLabelCode(sendData, "stockSpec", stock.getMatrSpec().replaceAll("~", "～"));
                    //库存批次
                    sendData = replaceLabelCode(sendData, "stockLot", stock.getLot());
                    //库存生产日期
                    sendData = replaceLabelCode(sendData, "stockMfgDate", DateUtils.format(stock.getMfgDate(), "yyyy-MM-dd"));
                    //库存有效期
                    sendData = replaceLabelCode(sendData, "stockExpDate", DateUtils.format(stock.getExpDate(), "yyyy-MM-dd"));
                    //生产厂商
                    sendData = replaceLabelCode(sendData, "stockMfgName", stock.getMfgName());
                    //条形码 SN
                    sendData = replaceLabelCode(sendData, "barcodeSn", stock.getSn());
                    //二维码 SN
                    sendData = replaceLabelCode(sendData, "qrcodeSn", stock.getSn());

                    //条形码 rfid
                    sendData = replaceLabelCode(sendData, "barcodeRFID", stock.getSn());
                    //二维码 rfid
                    sendData = replaceLabelCode(sendData, "qrcodeRFID", stock.getSn());
                    numSuccess += 1;
                    lodoopPrintRFIDDTO = new LodoopPrintRFIDDTO(sendData, msgPre, printer, stock);
                    if (printRFIDAndWait(printerConnection, lodoopPrintRFIDDTO)) {
                        logger.info("打印任务从此处暂停，SN：" + stock.getSn());
                        breakMsg = "打印异常-连接超时-请调整打印机超时时间 单个打印调用，超过最大时间。打印任务从SN：" + stock.getSn() + " 处暂停，请手动检查, 如果" + stock.getSn() + "已打出，请手动绑定对应RFID。";
                        break;
                    }
                }

                stockService.updatePrintNum(lodoopPrintRFIDDTO.getSnArrSuccess());
                if (StringUtils.isNotBlank(breakMsg)) {
                    result = RestResult.error(breakMsg + "成功数量:" + (numSuccess - 1));
                } else {
                    result.setMsg("操作成功，数量:" + numSuccess);
                }

            } catch (ConnectionException e) {
                result.setCode(ErrorCode.COMMON_ERROR);
                result.setMsg("打印异常-打印机不可用-打印机【" + printer.getName() + "】网络连接异常");
                result.setSuccess(false);
                logger.error("printRFID.error", e);
            } catch (ZebraPrinterLanguageUnknownException e) {
                result.setCode(ErrorCode.COMMON_ERROR);
                result.setMsg("打印异常-打印机不可用-打印机【" + printer.getName() + "】连接失败");
                result.setSuccess(false);
                logger.error("printRFID.error", e);
            } catch (BusinessException e) {
                result.setCode(ErrorCode.COMMON_ERROR);
                result.setMsg(e.getMessage());
                result.setSuccess(false);
                logger.error("printRFID.error", e);
            } catch (Exception e) {
                result.setCode(ErrorCode.COMMON_ERROR);
                result.setMsg("打印异常-服务器异常，打印失败");
                result.setSuccess(false);
                logger.error("printRFID.error", e);
            } finally {
                try {
                    if (printerConnection != null) {
                        printerConnection.close();
                    }
                    if (zebraPrinter != null) {
                        zebraPrinter = null;
                    }

                } catch (Exception e) {
                    logger.error("printRFID.error", e);
                }
            }
            return result;
        }
    }
}
